package com.dbflow5.query.property;

import com.dbflow5.config.FlowManager;
import com.dbflow5.converter.TypeConverters;
import com.dbflow5.query.NameAlias;
import com.dbflow5.query.Operator;

/**
 * Description: Provides convenience methods for [TypeConverter] when constructing queries.
 *
 * @author Andrew Grosner (fuzz)
 */

public class TypeConvertedProperty<T, V> extends Property<V> {

    private TypeConvertedProperty<V, T> databaseProperty = null;

    private final boolean convertToDB;
    private final TypeConverterGetter getter;

    @Override
    public Class<?> table() {
        return super.table;
    }

    /**
     * Generated by the compiler, looks up the type converter based on [ModelAdapter] when needed.
     * This is so we can properly retrieve the type converter at any time.
     */
    public interface TypeConverterGetter {
        TypeConverters.TypeConverter<?, ?> getTypeConverter(Class<?> modelClass);
    }

    @Override
    public Operator<V> operator() {
        return Operator.op(nameAlias(), table, getter, convertToDB);
    }

    public TypeConvertedProperty(Class<?> table, NameAlias nameAlias, boolean convertToDB, TypeConverterGetter getter) {
        super(table, nameAlias);
        this.convertToDB = convertToDB;
        this.getter = getter;
    }

    public TypeConvertedProperty(Class<?> table, String columnName, boolean convertToDB, TypeConverterGetter getter) {
        super(table, NameAlias.nameAlias(columnName));
        this.convertToDB = convertToDB;
        this.getter = getter;
    }

    @Override
    public TypeConvertedProperty<T, V> withTable() {
        NameAlias nameAlias = this.nameAlias()
                .newBuilder()
                .withTable(FlowManager.getTableName(table))
                .build();
        return new TypeConvertedProperty<>(this.table, nameAlias, this.convertToDB, this.getter);
    }

    /**
     * A new [Property]
     * @return A new [Property] that corresponds to the inverted type of the [TypeConvertedProperty].
     * Provides a convenience for supplying type converted methods within the DataClass of the [TypeConverter]
     */
    public Property<T> invertProperty() {
        if(databaseProperty == null){
            TypeConvertedProperty<V, T> property = new TypeConvertedProperty<>(table, nameAlias(), !convertToDB, modelClass -> getter.getTypeConverter(modelClass));
            databaseProperty = property;
            return property;
        }
        return databaseProperty;
    }

    @Override
    public TypeConvertedProperty<T, V> withTable(NameAlias tableNameAlias) {
        NameAlias nameAlias = this.nameAlias()
                .newBuilder()
                .withTable(tableNameAlias.tableName)
                .build();
        return new TypeConvertedProperty<>(this.table, nameAlias, this.convertToDB, this.getter);
    }
}
